package com.ccsoft.zknote.service.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.mail.MailUtil;
import cn.hutool.json.JSONUtil;
import com.ccsoft.zknote.dao.IUserDao;
import com.ccsoft.zknote.dao.IUserLogDao;
import com.ccsoft.zknote.exception.ServiceException;
import com.ccsoft.zknote.pojo.User;
import com.ccsoft.zknote.pojo.UserLog;
import com.ccsoft.zknote.pojo.system.BusinessException;
import com.ccsoft.zknote.service.IUserService;
import com.ccsoft.zknote.util.SystemUtils;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.ccsoft.zknote.pojo.table.Tables.USER;

/**
 * @author chanchaw
 * @create 2023-07-08 22:28
 */
@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private IUserDao dao;
    @Autowired
    private IUserLogDao userLogDao;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Transactional
    @Override
    public Map<String, Object> login(String email, String password) throws ServiceException {
        List<User> users = null;
        try {
            QueryWrapper queryWrapper = QueryWrapper.create()
                    .select(USER.ID,USER.EMAIL,USER.NICKNAME,USER.HEAD_PIC,USER.LEVEL,USER.STATUS,USER.TIME)
                    .where(USER.EMAIL.eq(email))
                    .and(USER.PASSWORD.eq(password));
            users = dao.selectListByQuery(queryWrapper);
        }catch (Exception e){
            throw new BusinessException("使用邮箱密码查询登录账号时出现SQL异常！");
        }

        if(users == null || users.size() == 0) throw new BusinessException("账号或密码错误！");
        if(users.size() > 1) throw new BusinessException("邮箱已经注册过，不可重复注册！");

        User user = users.get(0);
        Integer status = Optional.ofNullable(user.getStatus()).orElse(1);
        if(status.equals(0)) throw new BusinessException("账号被锁定！");

        // 写入用户登录日志
        UserLog userLog = UserLog.builder().event("邮箱账号登录成功").userId(user.getId()).desc("登录").time(new Date()).build();
        int insertLoginLogRet = 0;
        try{
            insertLoginLogRet = userLogDao.insertSelective(userLog);
        }catch(Exception e){
            e.printStackTrace();
            throw new BusinessException("写入账号登录日志时出现SQL异常！");
        }

        if(insertLoginLogRet != 1) throw new BusinessException("写入账号登录日志失败！");

        // 生成 userToken
        String userTokenKey = "";
        try {
            userTokenKey = "userToken:" + SystemUtils.getUUID();
            redisTemplate.opsForValue().set(
                    userTokenKey,
                    JSONUtil.toJsonStr(user),
                    14,
                    TimeUnit.DAYS
            );
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("登录后写入redis失败！");
        }

        Map<String, Object> map = new HashMap();
        map.put("user",user);
        map.put("userToken",userTokenKey);

        return map;
    }

    @Override
    public Boolean isExists8Email(String email){
        QueryWrapper queryWrapper = QueryWrapper.create().where(USER.EMAIL.eq(email));
        List<User> users = dao.selectListByQuery(queryWrapper);
        return users.size() > 0 ? true : false;
    }

    /**
     * 使用邮箱、验证码注册新用户
     * @param email
     * @param vc
     * @return
     */
    @Override
    public User register(String email, String vc,String vcTokenKey){
        // 1. 检测重复邮箱
        if(isExists8Email(email)) throw new BusinessException("邮箱" + email + "已经注册过，不可重复使用！");

        // 2. 检测验证码
        String s = redisTemplate.opsForValue().get(vcTokenKey);
        Assert.notEmpty(s,"根据" +vcTokenKey+ "未能查询到对应的token");
        if(!s.equalsIgnoreCase(vc)) throw new BusinessException("验证码错误！");

        String pwd = RandomUtil.randomString(6);// 生成长度为6的随机字符串作为默认密码
        String pwdMd5 = SecureUtil.md5(pwd);// 加密密码
        User user = User.builder().email(email).password(pwdMd5).time(new Date()).status(1)
                .pwd(pwd) // 保存明文密码
                .build();
        try {
            int i = dao.insertSelective(user);
        } catch (Exception e) {
            throw new BusinessException("新增用户时出现异常！");
        }

        // 3. 检测当前注册表单中的邮箱，防止修改邮箱账号后导致和验证码不匹配
        String[] tokenKeyArray = vcTokenKey.split(":");
        String vcEmail = tokenKeyArray[1];
        if(!vcEmail.equalsIgnoreCase(email)) throw new BusinessException("邮箱账号和验证码不匹配！");


        // 4. 新增注册日志
        UserLog userLog = UserLog.builder().userId(user.getId()).event("新用户注册").time(new Date()).build();
        try {
            userLogDao.insertSelective(userLog);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("写入注册日志时出现SQL异常！");
        }

        // 5. 发送邮件通知用户默认密码
        String emailContent = "<p>【天尘 note】尊敬的新用户<b style='font-size: 20px;color: blue'>" + email + "</b>：，</p>" +
                "<p>恭喜您注册成功，您的默认密码是：<b style='font-size:20px;color:blue'>"+pwd+"</b><p>";
        try {
            MailUtil.send(email,"天尘note账号注册成功通知",emailContent,true);
        } catch (Exception e) {
            throw new BusinessException("发送邮件失败（注册成功通知）");
        }
        return dao.selectOneByQuery(QueryWrapper.create().where(USER.ID.eq(user.getId())));
    }

    @Override
    public void logout(String userToken){
        Boolean delete = redisTemplate.delete(userToken);
    }
    @Override
    public User get8Email(String email){
        QueryWrapper queryWrapper = QueryWrapper.create()
                .select()
                .where(USER.EMAIL.eq(email));
        List<User> users = dao.selectListByQuery(queryWrapper);
        if(users == null || users.size() == 0) return null;
        return users.get(0);
    }

    @Override
    public List<User> getList(){
        return dao.selectAll();
    }

    @Override
    public User insert(User record){
        int i = dao.insertSelective(record);
        return record;
    }

    @Override
    public User save(User record){
        int i = dao.insertOrUpdate(record);
        return record;
    }
}
